package com.proxy.common.pool.impl;

import com.proxy.common.pool.IdleObjectQueue;
import com.proxy.common.pool.Pool;
import com.proxy.common.pool.PoolObject;
import com.proxy.common.pool.PooledObjectFactory;
import com.proxy.common.pool.common.PoolConfig;
import com.proxy.common.pool.common.PoolException;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;


/**
 * Created by liufish on 17/1/28.
 */
public class BasePool<T> implements Pool<T> {

    /**
     * 配置信息
     */
    PoolConfig config;

    /**
     * 对外工厂模式
     */
    PooledObjectFactory<T> factory;

    /**
     * 空闲存放队列
     */
    IdleObjectQueue<T> idleObjectQueue;

    /**
     * 当前总数
     */
    volatile int totalCount;

    /**
     * 是否已经关闭
     */
    volatile boolean isShutdown = false;

    /**
     * 存储
     */
    Map<T,PoolObject<T>> objectMap = new ConcurrentHashMap<>();

    public BasePool(final PoolConfig config,
                    PooledObjectFactory<T> factory){
        config.validatePropCorrelation();
        IdleObjectQueue idleObjectQueue = new BaseIdleObjectQueue<>(config);
        this.init(config,idleObjectQueue,factory);
    }


    public BasePool(final PoolConfig config,
                    IdleObjectQueue<T> idleObjectQueue,
                    PooledObjectFactory<T> factory) {
        this.init(config,idleObjectQueue,factory);
    }




    private void init(final PoolConfig config,
                      IdleObjectQueue<T> idleObjectQueue,
                      PooledObjectFactory<T> factory){
        this.config = config;
        this.idleObjectQueue = idleObjectQueue;
        this.factory = factory;
        //创建初始化个数的对象,并且放入对象池中
        for (int i = 0; i < config.getInitial(); i++) {
            try {
                idleObjectQueue.offer(this.createObject());
            } catch (Exception e) {
                throw new PoolException(e);
            }
        }


        //需要检测空闲状态的配置
        if (config.getTimeBetweenEvictionRunsMillis() > 0 && config.isTestWhileIdle()) {
            //回收线程,检查空闲线程的状态
            Thread idleChecker = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (!BasePool.this.isShutdown) {
                        try {
                            Thread.sleep(config.getTimeBetweenEvictionRunsMillis());
                            evictableIdle();
                        } catch (Exception ex) {

                        }
                    }
                }
            });
            idleChecker.setDaemon(true);
            idleChecker.start();

        }
    }


    @Override
    public T borrowObject() {
        return borrowObject(config.getMaxWaitMillisOnBorrow(), TimeUnit.MILLISECONDS);
    }

    @Override
    public T borrowObject(long timeout, TimeUnit unit) {
        try {
            PoolObject<T> poolObject = idleObjectQueue.poll(timeout, unit);

            if (poolObject == null) {

                //初始化值,小于最大活动值
                if(totalCount < config.getMaxActive()){
                    poolObject = this.createObject();
                    if(poolObject == null){
                        throw new PoolException("Cannot get a free object from the pool");
                    }
                }

            }
            //验证
            if (poolObject != null && config.isTestOnBorrow()) {

                //验证不通过
                if (!factory.validateObject(poolObject.getObject())) {
                    decreaseObject(poolObject);
                    throw new PoolException("TestOnBorrow poolObject validate fail");
                }
            }
            //借出去时间
            poolObject.getState().setLastBorrowAt(System.currentTimeMillis());
            return poolObject.getObject();
        } catch (Exception e) {
            throw new PoolException(e);
        }
    }

    @Override
    public void returnObject(T object) {
        if (object == null) {
            throw new PoolException("return poolObject, poolObject is null");
        }
        PoolObject<T> poolObject = objectMap.get(object);
        if (poolObject == null) {
            throw new PoolException("return poolObject, poolObject is null");
        }
        //归还验证
        if (config.isTestOnReturn()) {
            if (!this.validateObject(poolObject)) {
                decreaseObject(poolObject);
                //验证不通过
                throw new PoolException("return poolObject poolObject validate fail");
            }
        }
        //归还时间
        poolObject.getState().setLastReturnAt(System.currentTimeMillis());
        idleObjectQueue.offer(poolObject);
    }

    @Override
    public void shutdown() {
        while (this.idleObjectQueue.getIdleObjectsCount() > 0) {
            PoolObject<T> poolObject = idleObjectQueue.poll();
            if (poolObject != null) {
                decreaseObject(poolObject);
            }
        }
        isShutdown = true;
    }

    /**
     * 增加
     */
    private PoolObject<T> createObject() {
        T object = factory.createObject();
        PoolObject<T> poolObject = new BasePoolObject<>(object);
        //存储
        objectMap.put(poolObject.getObject(),poolObject);
        totalCount ++;
        return poolObject;
    }

    /**
     * 减少
     */
    private synchronized void decreaseObject(PoolObject<T> poolObject) {
        try {
            totalCount --;
            //设置对象无效
            poolObject.getState().setValid(false);
            factory.destroyObject(poolObject.getObject());
        }catch (Exception ex){
            throw new PoolException(ex);
        }finally {
            //废弃
            objectMap.remove(poolObject.getObject());
        }

    }

    /**
     * 检测对象
     */
    private synchronized void evictableIdle() {

        final long current = System.currentTimeMillis();

        //空闲队列过大,移除过久未被借出的对象。
        if(idleObjectQueue.getIdleObjectsCount() > config.getMinActive()){
            idleObjectQueue.iterators(new IdleObjectQueue.PoolCallback<T>() {
                @Override
                public void call(PoolObject<T> poolObject) {
                    //最大空闲周期
                    long timeAliveIdleMillis = current - poolObject.getState().getLastBorrowAt().get();
                    if(timeAliveIdleMillis >= config.getTimeAliveIdleMillis()){

                        //保证不移除过多
                        if(idleObjectQueue.getIdleObjectsCount() > config.getMinActive()){
                            decreaseObject(poolObject);
                            idleObjectQueue.remove(poolObject);
                        }
                    }
                }
            });
        }

        //心跳验证
        idleObjectQueue.iterators(new IdleObjectQueue.PoolCallback<T>() {
            @Override
            public void call(PoolObject<T> poolObject) {
                long time = current - poolObject.getState().getLastValidatedAt();
                //需要检测的对象
                if (time >= config.getTimeBetweenEvictionRunsMillis()) {
                    if (!validateObject(poolObject)) {
                        //自检校验失败,
                        decreaseObject(poolObject);
                        idleObjectQueue.remove(poolObject);
                    }
                }
            }
        });

        //最小活动个数验证,保证最小存活个数。
        int nowSize = objectMap.size();
        if(objectMap.size() < config.getMinActive()){
            int createCount = config.getMinActive() - nowSize;
            //放入对象池中
            for (int i = 0; i < createCount; i++) {
                try {
                    idleObjectQueue.offer(this.createObject());
                } catch (Exception e) {
                    throw new PoolException(e);
                }
            }
        }

    }

    /**
     * 验证
     */
    private boolean validateObject(PoolObject<T> poolObject){
        poolObject.getState().setLastValidatedAt(System.currentTimeMillis());
        return factory.validateObject(poolObject.getObject());
    }


}
